package com.jxbeidou.gateway.filter;

import com.jxbeidou.core.cache.CacheService;
import com.jxbeidou.core.constents.CacheConstants;
import com.jxbeidou.core.constents.EncryptConstants;
import com.jxbeidou.core.constents.SecurityConstants;
import com.jxbeidou.core.crypto.util.EncryptUtil;
import com.jxbeidou.core.crypto.util.SaltUtil;
import com.jxbeidou.core.entity.SecurityUserVO;
import com.jxbeidou.core.enums.EncryptMode;
import com.jxbeidou.core.enums.SecurityResultCode;
import com.jxbeidou.core.exception.ErrorCode;
import com.jxbeidou.core.util.HttpUtil;
import com.jxbeidou.core.util.JsonUtil;
import com.jxbeidou.core.util.JwtUtil;
import com.jxbeidou.core.util.UUIDUtil;
import com.jxbeidou.gateway.service.ConfigAttributeService;
import com.jxbeidou.gateway.service.SecurityIgnoreUrlService;
import com.jxbeidou.gateway.util.ReactiveHttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ObjectUtils;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import jakarta.annotation.Resource;
import java.util.Set;

/**
 * 网关鉴权
 * 
 * @author ruoyi
 */
@Component
@Slf4j
public class AuthFilter implements GlobalFilter, Ordered {

    @Resource
    private CacheService cacheService;

    @Resource
    private ConfigAttributeService configAttributeService;

    @Resource
    private SecurityIgnoreUrlService securityIgnoreUrlService;

    private static final EncryptMode ENCRYPT_MODE = EncryptMode.DES;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("网关过滤器");
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpRequest.Builder mutate = request.mutate();
        if (request.getMethod() == HttpMethod.OPTIONS) {
            log.debug("OPTIONS请求不拦截");
            return chain.filter(exchange);
        }
        String url = request.getURI().getPath();
        String key = generateKey();
        log.debug("网关过滤器，URL：[{}]", url);
        // 跳过不需要验证的路径
        if (ignore(url)) {
            log.debug("[网关过滤器]白名单跳过，url:[{}]", url);
            key = ignoreKey(key);
            addHeader(mutate, CacheConstants.GATEWAY_REQUEST_HEADER_NAME, key);
            key = DigestUtils.md5Hex(EncryptUtil.encrypt(ENCRYPT_MODE, key, key));
            addHeader(mutate, key, url);
            //清除登录token
            removeHeader(mutate, SecurityConstants.AUTHENTICATION);
            // 内部请求来源参数清除
            removeHeader(mutate, SecurityConstants.FROM_SOURCE);
            return chain.filter(exchange.mutate().request(mutate.build()).build());
        }
        String token = ReactiveHttpUtil.getToken(request);
        if (StringUtils.isEmpty(token)) {
            return unauthorizedResponse(exchange, SecurityResultCode.ACCOUNT_TOKEN_NULL_FAILED);
        }
        SecurityUserVO user;
        String userJson = "";
        try {
            userJson = JwtUtil.verifyTokenByHsStr(token);
            user = JsonUtil.parseObject(userJson, SecurityUserVO.class);
        } catch (Exception e) {
            return unauthorizedResponse(exchange, SecurityResultCode.ACCOUNT_TOKEN_FAILED);
        }
        if (user == null) {
            return unauthorizedResponse(exchange, SecurityResultCode.ACCOUNT_TOKEN_FAILED);
        }
        String validate = cacheService.getString(CacheConstants.SYS_LOGIN_USER_KEY + user.getUserKey());
        if (StringUtils.isEmpty(validate)) {
            return unauthorizedResponse(exchange, SecurityResultCode.CREDENTIALS_EXPIRED_FAILED);
        }
        if ( (!user.getSuperAdmin()) && configAttributeService.validateAuthority(url, user.getRole())) {
            log.debug("[网关鉴权过滤器] username:[{}]没有权限访问url:[{}]", url);
            log.debug("[网关鉴权过滤器] role:[{}]", user.getRole());
            return unauthorizedResponse(exchange, SecurityResultCode.UNAUTHORIZED_FAILED);
        }
        addHeader(mutate, CacheConstants.GATEWAY_REQUEST_HEADER_NAME, key);
        key = DigestUtils.md5Hex(EncryptUtil.encrypt(ENCRYPT_MODE, key, key));
        addHeader(mutate, key, EncryptUtil.encrypt(ENCRYPT_MODE, key, userJson));
        //清除登录token
        removeHeader(mutate, SecurityConstants.AUTHENTICATION);
        // 内部请求来源参数清除
        removeHeader(mutate, SecurityConstants.FROM_SOURCE);
        return chain.filter(exchange.mutate().request(mutate.build()).build());
    }

    private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value) {
        if (value == null) {
            return;
        }
        log.debug("添加Header信息:name:[{}], value[{}]", name, value);
        String valueStr = value.toString();
        String valueEncode = HttpUtil.urlEncode(valueStr);
        mutate.header(name, valueEncode);
    }

    private void removeHeader(ServerHttpRequest.Builder mutate, String name) {
        log.debug("清除Header:[{}]", name);
        mutate.headers(httpHeaders -> httpHeaders.remove(name)).build();
    }

    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
        return ReactiveHttpUtil.responseResult(exchange.getResponse(), msg);
    }

    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, ErrorCode errorCode) {
        log.error("[鉴权异常处理]请求路径:{}, error-code:[{}], error:[{}]", exchange.getRequest().getPath(), errorCode.getCode(), errorCode.getMessage());
        return ReactiveHttpUtil.responseJson(exchange.getResponse(), errorCode);
    }

    /**
     * 白名单判断
     * @Author 杜晓祥
     * @Date 2023/12/20
     * @param url
     * @return boolean
     */
    private boolean ignore (String url) {
        if (securityIgnoreUrlService == null) {
            return false;
        }
        Set<String> ignoreUrl = securityIgnoreUrlService.loadIgnoreUrl();
        if (ObjectUtils.isEmpty(ignoreUrl)) {
            return false;
        }
        PathMatcher pathMatcher = new AntPathMatcher();
        for (String ig : ignoreUrl) {
            if (pathMatcher.match(url, ig)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 生成唯一校验KEY
     * @Author 杜晓祥
     * @Date 2024/1/26
     * @return java.lang.String
     */
    private String generateKey () {
        StringBuilder builder = new StringBuilder(EncryptConstants.HEADER_PREFIX);
        String s1 = ENCRYPT_MODE.getMode();
        builder.append(s1);
        builder.append(EncryptConstants.SECRET_SEPARATE);
        builder.append(EncryptUtil.encrypt(ENCRYPT_MODE, DigestUtils.md5Hex(s1), s1));
        builder.append(EncryptConstants.SECRET_SEPARATE);
        builder.append(UUIDUtil.simpleUUID());
        builder.append(EncryptConstants.SECRET_SEPARATE);
        builder.append(SaltUtil.generateString());
        return builder.toString();
    }

    private String ignoreKey (String key) {
        key = key.replaceFirst(EncryptConstants.HEADER_PREFIX, "");
        return EncryptConstants.HEADER_IGNORE_PREFIX + key;
    }

    @Override
    public int getOrder()
    {
        return -200;
    }
}